package com.x.compute.common.util;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.AviatorEvaluatorInstance;
import com.googlecode.aviator.Expression;
import com.googlecode.aviator.lexer.token.OperatorType;
import com.x.compute.common.constant.VarTypeEnum;
import com.x.compute.common.handle.*;
import com.x.compute.common.constant.StrConstant;
import com.x.compute.common.entity.VarHandleResult;
import com.x.compute.common.function.*;
import lombok.extern.log4j.Log4j2;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 计算工具类
 *
 * @author xuxin
 */
@Slf4j
public class ComputeUtils {

    public static Map<VarTypeEnum, VarTypeHandle> varHandleMap = new ConcurrentHashMap<>();

    static {
        //加载变量处理类
        varHandleMap.put(VarTypeEnum.STRING, new StringVarHandle());
        varHandleMap.put(VarTypeEnum.NUMBER, new NumberVarHandle());
        varHandleMap.put(VarTypeEnum.BOOLEAN, new BooleanVarHandle());
        varHandleMap.put(VarTypeEnum.DATE, new DateVarHandle());
        //初始化基本运算函数重载
        AviatorEvaluatorInstance instance = AviatorEvaluator.getInstance();
        instance.addOpFunction(OperatorType.EQ, new EqFunction());
        instance.addOpFunction(OperatorType.NEQ, new NeqFunction());
        instance.addOpFunction(OperatorType.GT, new GtFunction());
        instance.addOpFunction(OperatorType.GE, new GeFunction());
        instance.addOpFunction(OperatorType.LT, new LtFunction());
        instance.addOpFunction(OperatorType.LE, new LeFunction());
    }

    /**
     * 文本类型计算
     *
     * @param params     变量map
     * @param expression 表达式
     * @return
     */
    public static String executeString(Map<String, Object> params, String expression) {
        return execute(VarTypeEnum.STRING, params, expression);
    }

    /**
     * 数字类型计算
     *
     * @param params     变量map
     * @param expression 表达式
     * @return
     */
    public static String executeNumber(Map<String, Object> params, String expression) {
        String value = execute(VarTypeEnum.NUMBER, params, expression);
        Assert.isTrue(NumberUtil.isNumber(value),"计算值不属于数值类型");
        return value;
    }

    /**
     * 布尔类型计算
     *
     * @param params     变量map
     * @param expression 表达式
     * @return
     */
    public static boolean executeBoolean(Map<String, Object> params, String expression) {
        String value = execute(VarTypeEnum.BOOLEAN, params, expression);
        return BooleanUtil.toBoolean(value);
    }

    /**
     * 计算
     *
     * @param valueType  计算值类型
     * @param fieldMap   变量map
     * @param expression 表达式
     * @return
     */
    public static String execute(VarTypeEnum valueType, Map<String, Object> fieldMap, String expression) {
        Assert.notNull(expression, "计算公式不能为空");
        VarHandleResult handleResult = varHandleMap.get(valueType).handle(fieldMap, expression);
        if (handleResult.isReturn()) {
            return handleResult.getResult();
        }
        expression = handleResult.getExpression();
        try {
            Expression compile = AviatorEvaluator.compile(expression, true);
            String result = compile.execute(fieldMap).toString();
            return result;
        } catch (ArithmeticException e) {
            log.info("计算公式：{}，变量参数：{}，异常信息：{}", expression, JSONUtil.toJsonStr(fieldMap), e.getMessage());
            return BigDecimal.ZERO.toString();
        } catch (Exception e) {
            log.info("计算公式：{}，变量参数：{}，异常信息：{}", expression, JSONUtil.toJsonStr(fieldMap), e.getMessage());
            throw new IllegalArgumentException("计算公式不正确，请检查是否符合公式规则");
        }
    }

    /**
     * 变量名生成
     *
     * @param var      字段名称
     * @param businessId 业务id
     * @return
     */
    public static String getVarFullName(String var, String businessId) {
        return var + StrUtil.UNDERLINE + businessId;
    }

    /**
     * 列求和变量名生成
     *
     * @param tableId 业务id
     * @param var   字段名称
     * @return
     */
    public static String getColumnSumVarFullName(String tableId, String var) {
        return getColumnSumVarFullName(tableId, var, true);
    }

    /**
     * 列求和变量名生成
     *
     * @param tableId 业务id
     * @param var   字段名称
     * @param suf 是否追加后缀
     * @return
     */
    public static String getColumnSumVarFullName(String tableId, String var, boolean suf) {
        String fullName = tableId + StrUtil.UNDERLINE + var;
        if (suf){
            fullName = fullName + StrUtil.UNDERLINE + StrConstant.SUM_NAME;
        }
        return fullName;
    }

    /**
     * 列求和变量名生成
     *
     * @param var   字段名称
     * @return
     */
    public static String getColumnSumVarName(String var) {
        return var + StrUtil.UNDERLINE + StrConstant.SUM_NAME;
    }

    /**
     * 列变量名生成
     *
     * @param tableId 业务id
     * @param var   字段名称
     * @return
     */
    public static String getColumnVarFullName(String tableId, String var) {
        return tableId + StrUtil.UNDERLINE + var + StrUtil.UNDERLINE + StrConstant.COLUMN_NAME;
    }

    /**
     * 获取表达式中引用的变量集合
     *
     * @param valueType  值类型
     * @param expression 表达式
     * @return
     */
    public static List<String> getVarFullNames(VarTypeEnum valueType, String expression) {
        expression = varHandleMap.get(valueType).formatExpression(expression);
        try {
            return AviatorEvaluator.compile(expression).getVariableFullNames();
        } catch (Exception e) {
            log.info("计算公式{}编译异常，{}", expression, e.getMessage());
            if (e instanceof ArithmeticException) {
                throw new IllegalArgumentException("计算公式不符合公式规则，公式中涉及除法运算中的分母直接或间接地引用值为0");
            }
            throw new IllegalArgumentException("计算公式不符合公式规则");
        }
    }

}
